//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "aura.h"
#include "win32.h"
#include "private.h"

AURA_API AuraCondition_Create(
        /* [in] */ Aura_Handle * phCondition)
{
    CondDescr * pCond = NULL;
    assert(NULL != phCondition);

    pCond = (CondDescr *)Aura_malloc(sizeof(CondDescr));
    if (NULL == pCond) {
        return AURA_E_OUT_OF_MEMORY;
    }

    InitializeCriticalSection(&pCond->m_hLock);
    pCond->m_waitList.Initialize();

    *phCondition = (Aura_Handle)pCond;

    return AURA_NOERROR;
}

AURA_API AuraCondition_Wait(
        /* [in] */ Aura_Handle hCondition,
        /* [in] */ Aura_Handle hMutex,
        /* [in] */ Aura_Millisecond32 timeOut,
        /* [out] */ Aura_WaitResult * pResult)
{
    ThreadDescr * pSelf = GetCurrentThreadDescr();
    MutexDescr * pMutex = (MutexDescr *)hMutex;
    CondDescr  * pCond  = (CondDescr *)hCondition;
    DWORD dwRes = 0;
    Aura_ECode ec = AURA_NOERROR;

    if (pMutex->m_pOwner != pSelf) {
        return AURA_E_NOT_OWNER;
    }

    EnterCriticalSection(&pCond->m_hLock);
    pSelf->m_eventState = AURA_UNSIGNALED;
    pCond->m_waitList.InsertLast(&pSelf->m_inWaitList);
    LeaveCriticalSection(&pCond->m_hLock);

    AuraMutex_Unlock(hMutex);
    dwRes = WaitForSingleObjectEx(pSelf->m_hThread, timeOut, TRUE);
    switch (dwRes) {
        case WAIT_IO_COMPLETION:
            if (AURA_SIGNALED == pSelf->m_eventState) {
                *pResult = Aura_WaitResult_OK;
            }
            else {
                EnterCriticalSection(&pCond->m_hLock);
                pSelf->m_inWaitList.Detach();
                LeaveCriticalSection(&pCond->m_hLock);
                *pResult = Aura_WaitResult_Interrupted;
            }
            break;
        case WAIT_TIMEOUT:
            EnterCriticalSection(&pCond->m_hLock);
            if (!pSelf->m_inWaitList.IsEmpty()) {
                pSelf->m_inWaitList.Detach();
            }
            LeaveCriticalSection(&pCond->m_hLock);
            *pResult = Aura_WaitResult_TimedOut;
            break;
        case WAIT_OBJECT_0:
            // never reached here.
            assert(0);
            break;
        case WAIT_ABANDONED:
        default:
            ec = GetLastECodeError();
            break;
    }
    Aura_WaitResult wr;
    AuraMutex_Lock(hMutex, INFINITE, &wr);

    return ec;
}

STATIC VOID CALLBACK PulseFunc(ULONG_PTR dwParam)
{
    // empty function.
}

AURA_API AuraCondition_Pulse(
        /* [in] */ Aura_Handle hCondition)
{
    CondDescr * pCond = (CondDescr *)hCondition;
    ThreadDescr * pThread = NULL;
    DLinkNode * pNode = NULL;
    DWORD dwRes = 0;
    Aura_ECode ec = AURA_NOERROR;

    assert(NULL != pCond);
    EnterCriticalSection(&pCond->m_hLock);
    if (!pCond->m_waitList.IsEmpty()) {
        pNode = pCond->m_waitList.Next();
        pNode->Detach();
        pNode->Initialize();
    }
    LeaveCriticalSection(&pCond->m_hLock);

    if (NULL != pNode) {
        pThread = THIS(pNode, ThreadDescr, m_inWaitList);
        pThread->m_eventState = AURA_SIGNALED;
        dwRes = QueueUserAPC(PulseFunc,
                            pThread->m_hThread,
                            (ULONG_PTR)pThread);
        if (0 == dwRes) {
            ec = GetLastECodeError();
        }
    }

    return ec;
}

AURA_API AuraCondition_PulseAll(
        /* [in] */ Aura_Handle hCondition)
{
    CondDescr * pCond = (CondDescr *)hCondition;
    ThreadDescr * pThread = NULL;
    assert(NULL != pCond);

    //TODO: should be more efficient
    EnterCriticalSection(&pCond->m_hLock);
    while(!pCond->m_waitList.IsEmpty()) {
        pThread = THIS(pCond->m_waitList.Next(), ThreadDescr, m_inWaitList);
        pThread->m_inWaitList.Detach();
        pThread->m_inWaitList.Initialize();
        pThread->m_eventState = AURA_SIGNALED;
        QueueUserAPC(PulseFunc, pThread->m_hThread, (ULONG_PTR)pThread);
    }
    pCond->m_waitList.Initialize();
    LeaveCriticalSection(&pCond->m_hLock);

    return AURA_NOERROR;
}

AURA_API AuraCondition_Destroy(
        /* [in] */ Aura_Handle hCondition)
{
    CondDescr * pCondDescr = (CondDescr *)hCondition;
    assert(NULL != pCondDescr);

    AuraCondition_PulseAll(hCondition);
    DeleteCriticalSection(&pCondDescr->m_hLock);
    pCondDescr->m_waitList.Initialize();

    Aura_free((void *)pCondDescr);

    return AURA_NOERROR;
}

